`ifndef NewLabel_IMGSRC_AGENT_SV
`define NewLabel_IMGSRC_AGENT_SV

import ovm_pkg::*;
`include "ovm_macros.svh"
import	definitions::*;

interface ImgSrc0_if(input logic clk, rst_n);

	logic [7:0]	data_in;
	logic rd_en;
	logic rd_full;
	logic rd_empty;
	logic [1:0]	rd_status;
	int data_id;

 	modport drv(
		input clk, rst_n,
		output rd_full, rd_empty, rd_status, data_in, data_id,
		input rd_en
  	);
 	   
endinterface

interface ImgSrc1_if(input logic clk, rst_n);

	logic [23:0]	data_out;
	logic wr_en;
	logic wr_empty;
	logic wr_full;
	logic [1:0]	wr_status;
	//int data_id;

 	modport drv(
		input clk, rst_n,
		output wr_full, wr_empty, wr_status,
		input wr_en, data_out
  	);
 	   
endinterface

////////////////////////// items /////////////////////////
class tr_data extends ovm_sequence_item;
	rand logic[23:0] data_dst;
	rand logic[7:0]	data_src;
	rand Bus_OP_t op;
	rand int trans_delay;
	rand int data_id;
	rand bit last_data;

	constraint trans_delay_con{trans_delay>=0; trans_delay<=30;}

	`ovm_object_utils_begin(tr_data)
		`ovm_field_int(data_dst, OVM_ALL_ON + OVM_HEX)
		`ovm_field_int(data_src, OVM_ALL_ON + OVM_HEX)
		`ovm_field_enum(Bus_OP_t, op, OVM_ALL_ON + OVM_HEX)
		`ovm_field_int(trans_delay, OVM_ALL_ON + OVM_HEX)
		`ovm_field_int(data_id, OVM_ALL_ON + OVM_HEX)
		`ovm_field_int(last_data, OVM_ALL_ON + OVM_HEX)
	`ovm_object_utils_end

	function new (string name = "tr_data");
		super.new(name);
	endfunction : new

	virtual function void do_print(ovm_printer printer);
		super.do_print(printer);
	endfunction : do_print
	
endclass : tr_data

////////////////////////// drivers /////////////////////////
class NewLabel_imgsrc_driver extends ovm_driver #(tr_data);
	virtual ImgSrc0_if.drv src_if;
	virtual ImgSrc1_if.drv dst_if;
	`ovm_component_utils(NewLabel_imgsrc_driver)

	function new(string name, ovm_component parent);
		super.new(name, parent);
	endfunction : new

	function void assign_vi(virtual interface ImgSrc0_if src_if, virtual interface ImgSrc1_if dst_if);
	  this.src_if = src_if; 
	  this.dst_if = dst_if;
	endfunction : assign_vi

	task run();
		init_signals();

		forever begin
		  seq_item_port.get_next_item(req);
		  $cast(rsp, req.clone());

		  drive_transfer(req);
		        
		  rsp.set_id_info(req);
		  seq_item_port.item_done(rsp);
		end
	endtask : run

	task init_signals();
		time run_delay;
		assert(get_config_int("run_delay", run_delay));
		#run_delay;
		wait(src_if.rst_n);
		src_if.data_in <= '0;
		src_if.rd_full <= 1'b0;
		src_if.rd_empty <= 1'b1;
		src_if.rd_status <= '0;
		
		dst_if.wr_full <= 1'b0;
		dst_if.wr_empty <= 1'b1;
		dst_if.wr_status <= '0;
	endtask : init_signals

	task drive_transfer(tr_data trans);
		if(trans.trans_delay>0)
		begin
			#trans.trans_delay;    
		end

		if(trans.op == WRITE)
		begin
			ovm_report_info("imgsrc0", "Driving...", OVM_HIGH);
	    	ovm_report_info("imgsrc0", $psprintf("Detail:\n%s", trans.sprint()), OVM_FULL);

			fork
			@(posedge src_if.clk)
		    begin
		    	src_if.rd_empty <= '0;
	    		if(trans.last_data==1)
	    			src_if.rd_empty <= 1'b1;
			end
			
		    @(posedge src_if.clk iff(src_if.rd_en===1'b1))
		    begin
	    		src_if.data_in <= trans.data_src;
	    		src_if.data_id = trans.data_id;
			end
			join

	    end
	    else if(trans.op == READ)
	    begin
	    	ovm_report_info("imgsrc1", "Driving...", OVM_HIGH);
	    	ovm_report_info("imgsrc1", $psprintf("Detail:\n%s", trans.sprint()), OVM_FULL);

			fork
			@(posedge dst_if.clk)
		    begin
		    	dst_if.wr_empty <= '0;
			end
			
		    @(posedge dst_if.clk iff(dst_if.wr_en===1'b1))
		    begin
				trans.data_dst = dst_if.data_out;
			end
			join
	    end
	    
	endtask : drive_transfer
	
endclass : NewLabel_imgsrc_driver

////////////////////////// sequencers /////////////////////////
class NewLabel_imgsrc_sequencer extends ovm_sequencer #(tr_data);
	`ovm_sequencer_utils(NewLabel_imgsrc_sequencer)

	function new(string name, ovm_component parent=null);
		super.new(name, parent);
		`ovm_update_sequence_lib_and_item(tr_data)
	endfunction

endclass : NewLabel_imgsrc_sequencer

////////////////////////// sequences /////////////////////////
virtual class NewLabel_imgsrc_base_sequence extends ovm_sequence #(tr_data);  
	function new(string name="NewLabel_imgsrc_base_seq");    
		super.new(name);  
	endfunction

	virtual task pre_body();    
		m_sequencer.ovm_report_info(get_type_name(), 
					$psprintf("%s pre_body() raising an ovm_test_done objection", 
					get_sequence_path()), OVM_MEDIUM);    
		ovm_test_done.raise_objection(this);  
	endtask  

	virtual task post_body();    
		m_sequencer.ovm_report_info(get_type_name(), 
					$psprintf("%s post_body() dropping an ovm_test_done objection",
					get_sequence_path()), OVM_MEDIUM);
		ovm_test_done.drop_objection(this);  
	endtask  
endclass : NewLabel_imgsrc_base_sequence

class Special_16X16_seq extends NewLabel_imgsrc_base_sequence;

  function new (string name = "Special_16X16_seq");
    super.new(name);
  endfunction : new
  
  `ovm_sequence_utils(Special_16X16_seq, NewLabel_imgsrc_sequencer)

  tr_data lli;
  string filename;
  int width=16, height=16;
  logic[7:0] src[16][16];
  logic[23:0]	dst[16][16];
  //src = new[height][width];
  //dst = new[height][width];
  int i,j,k=0;
  
  virtual task body();
    //$readmemh("./testImg/srcImage.txt", src);
    $readmemh("./testImg/srcImage_16x16_20.txt", src);

  	for(i=0; i<height; i++)
  	begin
  		for(j=0; j<width; j++, k++)
  		begin
			`ovm_create(lli)
			lli.data_src = src[i][j];
			lli.op = WRITE;
			lli.data_id = k;
			if(i*j==16*16)
				lli.last_data = 1;
			`ovm_send(lli)
			//wait_for_item_done();
			get_response(rsp);
		end
	end
	foreach(dst[i,j])
	begin
		`ovm_create(lli)
		lli.op = READ;
		`ovm_send(lli)
		get_response(rsp);
		dst[height-1-i][width-1-j] = rsp.data_dst;
	end
	/*for(i=0; i<height; i++)
  	begin
  		for(j=0; j<width; j++)
  		begin
			`ovm_create(lli);
			lli.data_src = src[i][j];
			lli.op = WRITE;
			`ovm_send(lli);
			wait_for_item_done();
		end
	end*/
  endtask : body

endclass : Special_16X16_seq


////////////////////////// AGENT /////////////////////////
class NewLabel_imgsrc_agent extends ovm_agent;
  ovm_active_passive_enum is_active = OVM_ACTIVE;
  
  `ovm_component_utils_begin(NewLabel_imgsrc_agent)
    `ovm_field_enum(ovm_active_passive_enum, is_active, OVM_ALL_ON)
  `ovm_component_utils_end
  
  NewLabel_imgsrc_sequencer imgsrc_seqr;
  NewLabel_imgsrc_driver imgsrc_driver;
  
  function new(string name, ovm_component parent=null);
    super.new(name, parent);
  endfunction
  
  virtual function void build();
    super.build();
    if (is_active == OVM_ACTIVE) 
    begin
      imgsrc_seqr = NewLabel_imgsrc_sequencer::type_id::create("imgsrc_seqr",this);
      imgsrc_driver = NewLabel_imgsrc_driver::type_id::create("imgsrc_driver",this);
    end
  endfunction : build
  
  virtual function void connect();
    super.connect();
    if(is_active == OVM_ACTIVE) 
    begin
      imgsrc_driver.seq_item_port.connect(imgsrc_seqr.seq_item_export);
    end
  endfunction : connect

  function void assign_vi(virtual interface ImgSrc0_if a_if, virtual interface ImgSrc1_if b_if);
  	if (is_active == OVM_ACTIVE)
  	begin
  		imgsrc_driver.assign_vi(a_if, b_if);
  	end
  endfunction : assign_vi
  
endclass : NewLabel_imgsrc_agent


`endif

